Framework.Search = {}

SEARCH_DEFAULT			= 0 
SEARCH_ALLOWMULTIPLE 	= 1 -- Allow multiple results
SEARCH_EXACTMATCH 		= 2 -- Only returns exact matches
SEARCH_EXCLUSIVE		= 4 -- Only return players where all the search parameters fit.

-- These are for searching these groups specifically. It is not necessary to include all 3 to search everyone.
SEARCH_NONADMINS		= 8  
SEARCH_ADMINS			= 16
SEARCH_SUPERADMINS		= 32

function Framework.Search.FindPlayer( ST_TA_Parameters, OP_FG_Flags, OP_TA_Players )
	local OP_FG_Flags = OP_FG_Flags or 0
	local Matches = {}
	
	if( type( ST_TA_Parameters ) != "table" ) then
		ST_TA_Parameters = { name = ST_TA_Parameters } -- Find by name by default
	end
	
	for k, v in pairs( OP_TA_Players or player.GetAll() ) do
		local x = OP_FG_Flags & SEARCH_EXCLUSIVE > 0 
		local a = ( OP_FG_Flags & SEARCH_ADMINS > 0 and !v:IsAdmin() ) or ( OP_FG_Flags & SEARCH_SUPERADMINS > 0 and !v:IsSuperAdmin() ) or ( OP_FG_Flags & SEARCH_NONADMINS > 0 and v:IsAdmin() )
		local b = ST_TA_Parameters.steamid and v:SteamID():lower() != ST_TA_Parameters.steamid:lower()
		local c = ST_TA_Parameters.uniqueid and v:UniqueID() != ST_TA_Parameters.uniqueid
		local d = ST_TA_Parameters.team and v:Team() != ST_TA_Parameters.team and team.GetName( v:Team() ) != ST_TA_Parameters.team
		local e = ST_TA_Parameters.ip and SERVER and v:IPAddress() != ST_TA_Parameters.ip
		local f = ST_TA_Parameters.name and OP_FG_Flags & SEARCH_EXACTMATCH > 0 and v:Nick() != ST_TA_Parameters.name
		local g = ST_TA_Parameters.name and OP_FG_Flags & SEARCH_EXACTMATCH == 0 and !v:Nick():find( ST_TA_Parameters.name )
		local h = ST_TA_Parameters.func and !ST_TA_Parameters.func( v, OP_FG_Flags )
		if ( (x and ( a or b or c or d or e or f or g or h)) or ( a or ( (b != false) and (c != false) and (d != false) and (e != false) and (f != false) and (g != false) and (h != false) ) ) ) then continue end
		
		if( OP_FG_Flags & SEARCH_ALLOWMULTIPLE > 0 ) then
			table.insert( Matches, v )
		else	
			return v
		end
	end
	if( #Matches > 0 ) then
		return Matches
	end
	return false
end

function Framework.Search.FindPlayers( ST_TA_Input, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( ST_TA_Input, OP_FG_Flags | SEARCH_ALLOWMULTIPLE, OP_TA_Players )
end

function Framework.Search.FindByName( ST_Name, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( { name = ST_Name }, OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.FindByTeam( NU_ST_Team, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( { team = NU_ST_Team }, OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.FindBySteamID( ST_SteamID, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( { steamid = ST_SteamID }, OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.FindByUniqueID( NU_UniqueID, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( { uniqueid = NU_UniqueID }, OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.FindByIPAddress( ST_IPAddress, OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( { ip = ST_IPAddress }, OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.GetAdmins( OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( "", SEARCH_ADMINS | SEARCH_ALLOWMULTIPLE | OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.GetSuperAdmins( OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( "", SEARCH_SUPERADMINS | SEARCH_ALLOWMULTIPLE | OP_FG_Flags, OP_TA_Players )
end

function Framework.Search.GetClients( OP_FG_Flags, OP_TA_Players )
	return Framework.Search.FindPlayer( "", SEARCH_NONADMINS | SEARCH_ALLOWMULTIPLE | OP_FG_Flags, OP_TA_Players )
end


function Framework.Search.FindFreeArea( VC_Position, VC_Dimensions, OP_NU_Intensity )
	local Half = VC_Dimensions / 2
	local Offsets = { 0, 1, -1 }
	local OP_NU_Intensity = ( OP_NU_Intensity && math.floor(math.max(OP_NU_Intensity,2)) or 2 ) / 2
	
	if( OP_NU_Intensity >= 20 ) then
		Framework.IO.Warning( "Framework.Search.FindFreeArea is being called with a dangerously high intensity (" .. OP_NU_Intensity*2 .. "), this could cause noticeable lag" )
	end
	
	for mul = 0.5, OP_NU_Intensity, 0.5 do
		for _, z in pairs( Offsets ) do
			for _, y in pairs( Offsets ) do
				for _, x in pairs( Offsets ) do
					local Local = ( VC_Dimensions * Vector( x, y, z ) * mul )
					local Pos = VC_Position + Local
					-- Using and here allows us to stop in the middle of this expression if either of these traces hit anything, whereas or would've evaluated all traces before coming to a conclusion.
					-- THE MORE YOU KNOW! --------*
					if( !util.TraceLine( { start = Pos + Vector(  	   0,      0,  Half.z ), endpos = Pos + Vector(       0,       0, -Half.z ) } ).Hit and
						!util.TraceLine( { start = Pos + Vector(  Half.x,  Half.y,  Half.z ), endpos = Pos + Vector(  Half.x, -Half.y, -Half.z ) } ).Hit and
						!util.TraceLine( { start = Pos + Vector(  Half.x, -Half.y,  Half.z ), endpos = Pos + Vector( -Half.x, -Half.y, -Half.z ) } ).Hit and
						!util.TraceLine( { start = Pos + Vector( -Half.x, -Half.y, Half.z ), endpos = Pos + Vector(  -Half.x,  Half.y, -Half.z ) } ).Hit and
						!util.TraceLine( { start = Pos + Vector( -Half.x,  Half.y, Half.z ), endpos = Pos + Vector(   Half.x,  Half.y, -Half.z ) } ).Hit ) then 
						return Pos
					end
				end
			end
		end
	end
	return false
end